package controller.validator;

import java.util.ArrayList;

import entity.Bid;
import entity.Section;
import entity.Student;
import exception.InputValidationErrors;

/**
 * Implements validation for adding, updating or deleting bid.
 * 
 * @author huangjm.2012, jaslin.wee.2012, huiwensong.2012
 * 
 */

public class BidValidator extends Validator {

	private String userId;
	private String courseCode;
	private String sectionCode;
	private String bidAmountStr;

	private double updatedEdollarForStudent = 0;
	private Bid validatedBid;
	private boolean hasPreviousBid = false;

	public Bid getBid() {
		return validatedBid;
	}

	public double getUpdatedEdollarForStudent() {
		return updatedEdollarForStudent;
	}

	public boolean hasPreviousBid() {
		return hasPreviousBid;
	}

	public BidValidator(String userId, String courseCode, String sectionCode,
			String bidAmountStr) {
		super();
		this.userId = userId;
		this.courseCode = courseCode;
		this.sectionCode = sectionCode;
		this.bidAmountStr = bidAmountStr;
	}

	public BidValidator(String userId, String courseCode, String sectionCode) {
		super();
		this.userId = userId;
		this.courseCode = courseCode;
		this.sectionCode = sectionCode;
	}

	@Override
	public void performValidationBeforeAdding() throws InputValidationErrors {

		/*
		 * Array list to store logic error messages
		 */
		ArrayList<String> logicErrors = new ArrayList<String>();

		/*
		 * Validation for invalid userid
		 */
		Student student = null;
		try {
			student = checkForStudent(userId);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_USERID);
		}

		/*
		 * Validation for invalid amount
		 */
		double bidAmount = 0;
		try {
			bidAmount = validateBidAmount(bidAmountStr);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_AMOUNT);
		}

		/*
		 * Validation for invalid course & invalid section
		 */
		Section section = null;
		try {
			checkForCourse(courseCode);

			try {
				section = checkForSection(courseCode, sectionCode);
			} catch (Exception e) {
				logicErrors.add(Validator.INVALID_SECTION);
			}

		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_CODE);
		}

		/*
		 * Throw exception or create entity
		 */
		if (!logicErrors.isEmpty()) {
			throw new InputValidationErrors(logicErrors);
		} else {

			/*
			 * Validation for not own school course
			 */
			try {
				checkForOwnSchoolCourseInRoundOne(section.getCourse(), student);
			} catch (Exception e) {
				logicErrors.add(Validator.NOT_OWN_SCHOOL_COURSE);
			}

			/*
			 * Validation for class timetable clash
			 */
			try {
				checkForSectionTimetableClashWithPreviousBidsIgnoringSameCourse(
						section, userId);
			} catch (Exception e) {
				logicErrors.add(Validator.CLASS_TIMETABLE_CLASH);
			}

			/*
			 * Validation for exam timetable clash
			 */
			try {
				checkForExamTimetableClashWithPreviousBidsIgnoringSameCourse(section.getCourse(),
						userId);
			} catch (Exception e) {
				logicErrors.add(Validator.EXAM_TIMETABLE_CLASH);
			}

			/*
			 * Validation for incomplete prerequisites
			 */
			try {
				checkForPreRequisitesCompletion(userId, courseCode);
			} catch (Exception e) {
				logicErrors.add(Validator.INCOMPLETE_PREREQUISITES);
			}

			/*
			 * Validation for already completed
			 */
			try {
				checkForPriorCourseCompletion(userId, courseCode);
			} catch (Exception e) {
				logicErrors.add(Validator.ALREADY_COMPLETED);
			}

			/*
			 * Validation for section limit reached
			 */
			try {
				checkForSectionBiddingLimit(userId);
			} catch (Exception e) {
				logicErrors.add(Validator.SECTION_LIMIT_REACHED);
			}

			/*
			 * Check for previous bid & not enough edollar
			 */
			double amountToDeduct = 0;
			try {
				Bid previousBid = checkForBid(userId, courseCode);
				hasPreviousBid = true;

				/*
				 * Validation for not enough edollar for updating old bid
				 */
				try {
					amountToDeduct = checkForEnoughEdollar(previousBid,
							bidAmount);
				} catch (Exception e) {
					logicErrors.add(Validator.NOT_ENOUGH_EDOLLAR);
				}

			} catch (Exception previousBidException) {
				/*
				 * Validation for not enough edollar for adding new bid
				 */
				try {
					amountToDeduct = checkForEnoughEdollar(student, bidAmount);
				} catch (Exception e) {
					logicErrors.add(Validator.NOT_ENOUGH_EDOLLAR);
				}
			}

			/*
			 * Validation for bid too low
			 */
			try {
				checkForMinBidAmountForSection(bidAmount, section);
			} catch (Exception e) {
				logicErrors.add(Validator.BID_TOO_LOW);
			}

			/*
			 * Validation for zero vacancy, coz, this is a new bid
			 */
			if (!hasPreviousBid) {
				try {
					checkForVacancy(section);
				} catch (Exception e) {
					logicErrors.add(Validator.NO_VACANCY);
				}
			}

			if (!logicErrors.isEmpty()) {
				throw new InputValidationErrors(logicErrors);
			} else {
				updatedEdollarForStudent = student.getEdollar()
						- amountToDeduct;
				validatedBid = new Bid(student, section, bidAmount);
			}

		}

	}

	@Override
	public void performValidationBeforeUpdating() throws InputValidationErrors {
		/*
		 * Array list to store logic error messages
		 */
		ArrayList<String> logicErrors = new ArrayList<String>();

		/*
		 * Validation for invalid amount
		 */
		double bidAmount = 0;
		try {
			bidAmount = validateBidAmount(bidAmountStr);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_AMOUNT);
		}

		/*
		 * Validation for invalid course & invalid section
		 */
		Section section = null;
		try {
			checkForCourse(courseCode);

			try {
				section = checkForSection(courseCode, sectionCode);
			} catch (Exception e) {
				logicErrors.add(Validator.INVALID_SECTION);
			}

		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_CODE);
		}

		/*
		 * Validation for invalid userid
		 */
		Student student = null;
		try {
			student = checkForStudent(userId);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_USERID);
		}

		/*
		 * Throw exception
		 */
		if (!logicErrors.isEmpty()) {

			throw new InputValidationErrors(logicErrors);

		} else {

			/*
			 * Validation for bid too low
			 */
			try {
				checkForMinBidAmountForSection(bidAmount, section);
			} catch (Exception e) {
				logicErrors.add(Validator.BID_TOO_LOW);
			}

			/*
			 * Validation for insufficient e$
			 */
			Bid previousBid = null;
			double amountToDeduct = 0;
			try {
				previousBid = checkForBid(userId, courseCode);
				hasPreviousBid = true;

				/*
				 * Validation for not enough edollar for updating old bid
				 */
				try {
					amountToDeduct = checkForEnoughEdollar(previousBid,
							bidAmount);
				} catch (Exception e) {
					logicErrors.add(Validator.INSUFFICIENT_E$);
				}

			} catch (Exception noPreviousBidException) {
				/*
				 * Validation for not enough edollar for adding new bid
				 */
				try {
					amountToDeduct = checkForEnoughEdollar(student, bidAmount);
				} catch (Exception e) {
					logicErrors.add(Validator.INSUFFICIENT_E$);
				}
			}

			/*
			 * Validation for class timetable clash
			 */
			try {
				checkForSectionTimetableClashWithPreviousBidsIgnoringSameCourse(
						section, userId);
			} catch (Exception e) {
				logicErrors.add(Validator.CLASS_TIMETABLE_CLASH);
			}

			/*
			 * Validation for exam timetable clash
			 */
			try {
				checkForExamTimetableClashWithPreviousBidsIgnoringSameCourse(section.getCourse(),
						userId);
			} catch (Exception e) {
				logicErrors.add(Validator.EXAM_TIMETABLE_CLASH);
			}

			/*
			 * Validation for incomplete prerequisites
			 */
			try {
				checkForPreRequisitesCompletion(userId, courseCode);
			} catch (Exception e) {
				logicErrors.add(Validator.INCOMPLETE_PREREQUISITES);
			}

			try {
				checkForRoundOpening();
			} catch (Exception e) {
				logicErrors.add(Validator.ROUND_ENDED);
			}

			/*
			 * Validation for already completed
			 */
			try {
				checkForPriorCourseCompletion(userId, courseCode);
			} catch (Exception e) {
				logicErrors.add(Validator.COURSE_COMPLETED);
			}

			/*
			 * Validation for course enrolled
			 */
			if (!hasPreviousBid
					|| (hasPreviousBid && previousBid.getBidRoundNo() != roundCtrl
							.getCurrentRound().getRoundNo()))
				try {
					checkForPriorCourseEnrollment(section.getCourse(), userId);
				} catch (Exception e) {
					logicErrors.add(Validator.COURSE_ENROLLED);
				}

			/*
			 * Validation for section limit reached
			 */
			if (!hasPreviousBid) {
				try {
					checkForSectionBiddingLimit(userId);
				} catch (Exception e) {
					logicErrors.add(Validator.SECTION_LIMIT_REACHED);
				}
			}

			/*
			 * Validation for not own school course
			 */
			try {
				checkForOwnSchoolCourseInRoundOne(section.getCourse(), student);
			} catch (Exception e) {
				logicErrors.add(Validator.NOT_OWN_SCHOOL_COURSE);
			}

			/*
			 * Validation for zero vacancy, coz, this is a new bid
			 */
			if (!hasPreviousBid) {
				try {
					checkForVacancy(section);
				} catch (Exception e) {
					logicErrors.add(Validator.NO_VACANCY);
				}
			}

			if (!logicErrors.isEmpty()) {
				throw new InputValidationErrors(logicErrors);
			} else {
				updatedEdollarForStudent = student.getEdollar()
						- amountToDeduct;
				validatedBid = new Bid(student, section, bidAmount);
			}
		}

	}

	@Override
	public void performValidationBeforeDeleting() throws InputValidationErrors {

		/*
		 * Array list to store logic error messages
		 */
		ArrayList<String> logicErrors = new ArrayList<String>();

		/*
		 * Validation for invalid course
		 */
		try {
			checkForCourse(courseCode);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_COURSE);
		}

		/*
		 * Validation for invalid userid
		 */
		Student student = null;
		try {
			student = checkForStudent(userId);
		} catch (Exception e) {
			logicErrors.add(Validator.INVALID_USERID);
		}

		/*
		 * Validation for invalid section
		 */
		try {
			checkForCourse(courseCode);
			try {
				checkForSection(courseCode, sectionCode);
			} catch (Exception e) {
				logicErrors.add(Validator.INVALID_SECTION);
			}

		} catch (Exception e) {
			// invalid course so, so no need to validate section
		}

		/*
		 * Validation for invalid section
		 */
		try {
			checkForRoundOpening();
		} catch (Exception e) {
			logicErrors.add(Validator.ROUND_ENDED);
		}

		Bid bid = null;
		if (logicErrors.isEmpty()) {
			/*
			 * Validation for no such bid
			 */
			try {
				bid = checkForBid(userId, courseCode, sectionCode);
			} catch (Exception e) {
				logicErrors.add(Validator.NO_SUCH_BID);
			}
		}

		if (!logicErrors.isEmpty()) {
			throw new InputValidationErrors(logicErrors);
		} else {
			validatedBid = bid;
			updatedEdollarForStudent = student.getEdollar()
					+ validatedBid.getBidAmount();
		}

	}

}
